/**
 * Implementation of the Physical Layer interface.
 *
 * @author Timothy Flynn (trflynn@wpi.edu)
 * @version April 11, 2012
 */
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
using std::cerr;
using std::cout;
using std::endl;

#include "PhysicalLayer.h"
#include "crc.h"

/**
 * Constructor to be used by server. Forks a new process for each incoming
 * client connection. The child process returns to calling function, but the
 * parent process never exits (unless fork() raises error).
 */
PhysicalLayer::PhysicalLayer(float err) {
	//cout << "  Starting Server PHY" << endl;
	srand(time(NULL));
	errPct = err;

	struct sockaddr_in servAddr;
	int servID;

	// Create the TCP socket
	if((servID = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
		cerr << "Server: Error with socket()" << endl;
		exit(1);
	}

	// Construct the server address structure
	memset(&servAddr, 0, sizeof(servAddr));
	servAddr.sin_family = AF_INET;
	servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servAddr.sin_port = htons(SERVER_PORT);

	// Set socket to be able to reuse this address
	setsockopt(servID, SOL_SOCKET, SO_REUSEADDR, (const void *)1, 4);

	// Bind socket to the server address
	if(bind(servID, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0) {
		cerr << "Server: Error with bind()" << endl;
		exit(1);
	}

	// Allow socket to listen for connections
	if(listen(servID, 100) < 0) {
		cerr << "Server: Error with listen()" << endl;
		exit(1);
	}

	while(true) {
		// Accept a client connection
		if((sktID = accept(servID, NULL, NULL)) < 0) {
			cerr << "Server: Error with accept()" << endl;
			exit(1);
		}

		// Fork new process for new connection
		int pid = fork();
		//cout << "  New connection from Client PHY" << endl;

		// Return if we are the child
		if(pid == 0) {
			return;
		} else if(pid == -1) {
			cerr << "Server: Error with fork()" << endl;
			exit(1);
		}
	}
}

/**
 * Constructor to be used by client. Simply finds the IP address of servName
 * and opens a connection to the server.
 */
PhysicalLayer::PhysicalLayer(const char *servName, float err) {
	//cout << "  Starting Client PHY" << endl;
	srand(time(NULL));
	errPct = err;

	struct sockaddr_in servAddr;
	struct hostent *servIP;

	// Translate server name to an IP address
	if(!(servIP = gethostbyname(servName))) {
		cerr << "Client: server name is unknown" << endl;
		exit(1);
	}

	// Construct the server address structure
	memset(&servAddr, 0, sizeof(servAddr));
	servAddr.sin_family = AF_INET;
	memcpy((char*)&servAddr.sin_addr, servIP->h_addr, servIP->h_length);
	servAddr.sin_port = htons(SERVER_PORT);

	// Create the TCP socket
	if((sktID = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		cerr << "Client: Error with socket()" << endl;
		exit(1);
	}

	// Connect to the server
	if(connect(sktID, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0) {
		cerr << "Client: Error with connect()" << endl;
		exit(1);
	}
	//cout << "  Connected to server PHY" << endl;
}

/**
 * Destructor. Close the socket.
 */
PhysicalLayer::~PhysicalLayer() {
	close(sktID);
}

/**
 * Run the physical layer thread. Cycle between trying to send a frame over the
 * network and trying to read a frame that was sent.
 */
void PhysicalLayer::run() {
	while(1) {
		sendFrame();
		receiveFrame();
	}
}

/**
 * Try to send a frame over the network, if one is available and we can get a
 * lock on the sendFrameM semaphore.
 */
void PhysicalLayer::sendFrame() {
	Frame *out = NULL;

	// Try to get lock (don't want to block)
	if(pthread_mutex_trylock(&sendFrameM) == 0) {
		// If a frame can be sent, grab it
		if(!sendFrameQ.empty()) {
			out = sendFrameQ.front();
			sendFrameQ.pop();
		}
		pthread_mutex_unlock(&sendFrameM);

		// Send the frame out
		if(out != NULL) {
			//cout << "  Sending frame to remote PHY: " << out->data << endl;
			// Do CRC error checking
			out->crc = doCRC(out->data);

			int len = FRAME_LEN + out->length;
			if(send(sktID, out->toString(), len, 0) != len) {
				cerr << "PL: Error with send()" << endl;
			}
		}
	}
}

/**
 * Try to receive a frame from the network, if input is available and ready.
 */
void PhysicalLayer::receiveFrame() {
	struct timeval timeout;
	timeout.tv_sec = 0;
	timeout.tv_usec = 100; // Might not need to wait this long

	fd_set set;
	FD_ZERO(&set);
	FD_SET(sktID, &set);

	// Use select() so recv() won't block
	while(select(sktID+1, &set, 0, 0, &timeout)) {
		if(FD_ISSET(sktID, &set)) {
			//cout << "  Received frame from remote PHY" << endl;
			int len = FRAME_LEN + MAX_FRAME_SZ;
			char *in  = (char *)malloc((len + 1) * sizeof(char));

			// Read the frame
			if((len = recv(sktID, in, len, 0)) <= 0) {
				exit(1); // Assume we disconnected
			}
			in[len] = '\0';

			// Add errors to data and undo CRC error checking
			Frame *f = new Frame(in);
			introduceError(f->data);
			f->crc = undoCRC(f->data, f->crc);
			//cout << "    Adding frame to receiveQ: " << f->data << endl;

			pthread_mutex_lock(&recvFrameM);
			recvFrameQ.push(f);
			pthread_mutex_unlock(&recvFrameM);
		}

		// Reset our bitset
		FD_ZERO(&set);
		FD_SET(sktID, &set);
	}
}

/**
 * With probability errPct, change some bytes around.
 */
void PhysicalLayer::introduceError(char *data) {
	float r = ((float)(rand() % 100) / 100.0f);

	if(r < errPct) {
		data[0] = '*';
	}
}
